#include "stdafx.h"
#include "Semaphore.h"
#ifdef WIN32
# include <Windows.h>
#else
# include <common/TimeSpec.h>
# include <errno.h>
#endif

namespace catan
{
	Semaphore::Semaphore(int initValue)
	{
#ifdef WIN32
		mSem = ::CreateSemaphore(0, initValue, 0x7FFFFFFF, 0);
#else
		sem_init(&mSem, 0, initValue);
#endif
	}

	Semaphore::~Semaphore()
	{
#ifdef WIN32
		::CloseHandle(mSem);
#else
		sem_destroy(&mSem);
#endif
	}

	LockState::Enum Semaphore::Acquire(long timeout)
	{
#ifdef WIN32
		DWORD ret = ::WaitForSingleObject(mSem, timeout);
		switch (ret)
		{
		case WAIT_OBJECT_0:
			return LockState::SUCCEEDED;
		case WAIT_TIMEOUT:
			return LockState::TIMEDOUT;
		case WAIT_ABANDONED:
			return LockState::OWNERTERM;
		default:
			return LockState::FAILED;
		}
#else
		int ret = 0;
		if (timeout == -1)
		{
			ret = sem_wait(&mSem);
		}
		else
		{
			TimeSpec t = TimeSpec::MakeTimeout(timeout);
			ret = sem_timedwait(&mSem, &t);
		}
		if (ret == 0)
		{
			return LockState::SUCCEEDED;
		}
		else 
		{
			switch (errno)
			{
			case ETIMEDOUT:
				return LockState::TIMEDOUT;
			default:
				return LockState::FAILED;
			}
		}
#endif
	}

	LockState::Enum Semaphore::TryAcquire()
	{
#ifdef WIN32
		DWORD ret = ::WaitForSingleObject(mSem, 0);
		switch (ret)
		{
		case WAIT_OBJECT_0:
			return LockState::SUCCEEDED;
		case WAIT_TIMEOUT:
			return LockState::TIMEDOUT;
		case WAIT_ABANDONED:
			return LockState::OWNERTERM;
		default:
			return LockState::FAILED;
		}
#else
		int ret = sem_trywait(&mSem);
		if (ret == 0)
		{
			return LockState::SUCCEEDED;
		}
		else
		{
			switch (errno)
			{
			case 0:
				return LockState::SUCCEEDED;
			case ETIMEDOUT:
				return LockState::TIMEDOUT;
			default:
				return LockState::FAILED;
			}
		}
#endif
	}

	void Semaphore::Release()
	{
#ifdef WIN32
		::ReleaseSemaphore(mSem, 1, 0);
#else
		sem_post(&mSem);
#endif
	}
}
	
